package com.client.koshercook.client;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasAlignment;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.VerticalPanel;

public class Model {
	
	//common data
	public ClientRequest request = new ClientRequest();		//the request to send to the server
	public ClientResponse response = new ClientResponse();		//last response received from client
	public List<String> validIngs = new LinkedList<String>(); 	//store ing names the server is familiar with
	public List<String> popularRecipes = new ArrayList<String>(); 	//list of the 5 most popular recipes

	//objects that model needs to update
	public final RecipesTable recipesTable = new RecipesTable();
	public final RecipeInfoPanel recipeInfoPanel = new RecipeInfoPanel();
	public final UserIngredientsTable userIngsTable = new UserIngredientsTable(Window.getClientHeight());
	public final Fridge fridge = new Fridge();
	public final LoginPanel loginPanel = new LoginPanel(fridge,Window.getClientWidth());
	public final BottomPanel bottomPanel = new BottomPanel();
	public final TopRated topRated = new TopRated();
	public HTML generalInfo = new HTML("..טוען");
	public EdgesPanel generalInfoPanel = new EdgesPanel(generalInfo);
		
	public static final int PAGE_SIZE = 10;
	
	public final PopupPanel loader;	//the loader shown while waiting for server's answer
	
	private final GreetingServiceAsync greetingService = GWT.create(GreetingService.class);
	
	private boolean connected = false;
	private String userNick = "אורח";
	private String defaultText = "";
	private boolean autoSearch = false;
	private boolean smallScreen = false;	
	
	//dialog box for user not log message
	private DialogBox dBoxMes = new DialogBox();
	private HTML logMessage = new HTML("עליך להיות מחובר בכדי ");
	
	/* constructor */
	public Model(){
		//building the loader
		loader = new PopupPanel();
		VerticalPanel vp = new VerticalPanel();
		vp.add(new HTML("..טוען"));
		vp.add(new Image("loader_1.gif"));
		loader.add(vp);
		loader.setStyleName("empty");
		loader.setModal(true);
		loader.setAnimationEnabled(true);
		
		Button ok = new Button("אישור");
		ok.addClickHandler(new ClickHandler(){
			public void onClick(ClickEvent event) {
				logMessage.setText("עליך להיות מחובר בכדי ");
				dBoxMes.hide();
			}
		});
		
		logMessage.setHorizontalAlignment(HasAlignment.ALIGN_CENTER);
		DockPanel dp = new DockPanel();
		dp.setHorizontalAlignment(HasAlignment.ALIGN_CENTER);
		dp.add(logMessage, DockPanel.NORTH);
		dp.add(ok, DockPanel.CENTER);
		dp.setSpacing(3);
		dBoxMes.add(dp);
		dBoxMes.setGlassEnabled(true);
		dBoxMes.center();
		dBoxMes.hide();
		
		//getting init data
		getInitData();
	}
	
	/* send request to server, and update the Response object */
	public void sendRequest(){
		
		loader.center();
		if(request.isIngsEmpty()){
			response.clear();
			recipesTable.clear();
			recipeInfoPanel.updateTable();
			loader.hide();
			return;
		}
		System.out.println("<<client: sending request>>");
		greetingService.greetServer(request, new AsyncCallback<ClientResponse>() {
			public void onFailure(Throwable caught) {
				System.out.println("Client error: failure in sendRequest() - request details:" + request.toString());
				loader.hide();
			}

			public void onSuccess(ClientResponse result) {
				response = result;
				recipesTable.update();
				recipeInfoPanel.updateTable();
				System.out.println("<<client: got " + result.size() + "recipes>>");
				loader.hide();
			}
		});
	}
	
	/** gets number of recipes, and all the valid ingredient names */
	private void getInitData(){
		loader.setGlassEnabled(true);
		loader.center();
		greetingService.getInitData(new AsyncCallback<List<String>>(){
			public void onFailure(Throwable caught) {
				System.out.println("Client error: failure getting init data");
				loader.setGlassEnabled(false);
				loader.hide();
			}

			public void onSuccess(List<String> result) {
				int numOfRecs = Integer.parseInt(result.get(0));
				popularRecipes.add(result.get(1));
				popularRecipes.add(result.get(2));
				popularRecipes.add(result.get(3));
				popularRecipes.add(result.get(4));
				popularRecipes.add(result.get(5));
				result.remove(5); result.remove(4); result.remove(3);
				result.remove(2); result.remove(1); result.remove(0);
							
				topRated.fillTable();
				
				for (String ingName : result) {
					validIngs.add(ingName);
					Client.oracle.add(ingName);
				}
				defaultText = " באתר "+validIngs.size()+" שמות רכיבים, ו-"+numOfRecs+" מתכונים כשרים ";
				setInfoText(defaultText);
				System.out.println("client ready");
				loader.setGlassEnabled(false);
				loader.hide();
				
				if (!connected)
					showWelcomePop();
			}
		});
	}
	
	/** add a single ingredient, and send request to server */
	public boolean addIngredient(String ingName){
		if(request.ingsContains(ingName)){	// if ingName is already in request, ignore
			return false;
		}
		request.addIng(ingName);
		if(autoSearch)
			sendRequest();
		return true;
	}
	
	/** add multiple ingredients, and send request to server */
	public boolean addIngredients(List<String> ingNames){
		boolean flag = false;
		for(String ingName: ingNames){
			if(request.ingsContains(ingName))
				continue;
			else{
				request.addIng(ingName);
				flag = true;
			}
		}
		if(autoSearch)
			sendRequest();
		return flag;
	}
	
	/** remove one ingredient from user ingredient list */
	public void removeIngredient(String ingName){
		request.deleteIng(ingName);
		if(autoSearch)
			sendRequest();
	}
	
	/** the following 4 methods change the search Kosher type according to the user request */
	/* all Kosher */
	public void setKosherAll(){
		request.setKosherAll();
		if(autoSearch)
			sendRequest();
	}
	
	/* Meat */
	public void setKosherMeat(){
		request.setKosherMeat();
		if(autoSearch)
			sendRequest();
	}
	
	/* Dairy */
	public void setKosherDairy(){
		request.setKosherDairy();
		if(autoSearch)
			sendRequest();
	}
	
	/* Parve */
	public void setKosherParve(){
		request.setKosherParve();
		if(autoSearch)
			sendRequest();
	}
	
	/** change an ingredient to be must ingredient */
	public void setIngredientMust(String ingName, Boolean set){
		request.setIngredientMust(ingName, set);
		if(autoSearch)
			sendRequest();
	}
	
	/** next page button behavior. display correct recipes */
	public void nextPage(){
		//check if this is last page
		if(    ( response.size() < PAGE_SIZE * 4 )	
			&& ( ((request.getPage() % 4) + 1) * PAGE_SIZE > response.size() ) )
			return;
		//else
		request.setPage(request.getPage()+1);
		Client.pageText.setText(String.valueOf(1+Client.model.request.getPage())+" עמוד");
		
		if(request.getPage() % 4 == 0){	//if we need to get new 40 pages, get them
			sendRequest();
		}else{							//if we're still in the same 40, only update table
			recipesTable.update();
		}
			
	}
	
	/** back page button behavior. display correct recipes */
	public void backPage(){
		//check if this is first page
		if(request.getPage()==0)
			return;
		//else
		request.setPage(request.getPage()-1);
		Client.pageText.setText(String.valueOf(1+Client.model.request.getPage())+" עמוד");
		if(Client.model.request.getPage() % 4 == 3){		//if we need to get new 40 pages, get them
			sendRequest();
		}else{						//if we're still in the same 40, only update table
			recipesTable.update();
		}
	}
	
	public void resetPage(){
		request.setPage(0);
		Client.pageText.setText("עמוד 1");
	}
	
	public boolean serverKnowsIngName(String ingName){
		return validIngs.contains(ingName);
	}
	
	/** add alternative ingredient to a recipe. update server.
	 * check for valid name, user registration and kosher fit. */
	public void addAlterToRecipe(String link, String original, String alter){
		greetingService.addAlterToRecipe(link, original, alter, new AsyncCallback<Integer>(){
			public void onFailure(Throwable caught) {
			}
			public void onSuccess(Integer result) {
				System.out.println("addAlterToRecipe got: "+result);
				if(result==1)
					setInfoTextAndPO("שם חלופי נקלט בהצלחה");
				else if(result==0)
					setInfoTextAndPO("עליך להיות מחובר כדי להציע שם חלופי");
				else if(result==-1)
					setInfoTextAndPO("השם החלופי שנשלח פוגע בכשרות המתכון");
			}
		});
	}

	/** send the recipe a user entered to the server and give the user feedback */ 
	public void sendNewRecipe(String recipeName, String instructions, String ingredients, String type){
		greetingService.sendNewRecipe(recipeName, instructions, ingredients, type, new AsyncCallback<Integer>(){
			public void onFailure(Throwable caught) {
			}
			public void onSuccess(Integer result) {
				System.out.println("sendNewRecipe got: "+result);
				if(result==1)
					setPO("המתכון נקלט בהצלחה");
				else if(result==0)
					setPO("עליך להיות מחובר כדי לשלוח מתכון");
				else if(result==-1)
					setPO("המתכון שנשלח אינו כשר");
			}
		});
	}
	
	public void addComment(String link, final String comment){
		greetingService.addComment(link, comment, new AsyncCallback<Boolean>(){
			public void onFailure(Throwable caught) {
			}
			public void onSuccess(Boolean result) {
				if(result){
					setInfoTextAndPO("תגובה נקלטה בהצלחה");
					recipeInfoPanel.addToCommentsDisplayWithRes(comment, userNick);
				}
				else
					setInfoTextAndPO("תקלה בקליטת התגובה");
			}			
		});
	}
	
	public void sendMail(String desMail, String content){
		greetingService.sendMail(desMail, content, new AsyncCallback<Boolean>(){
			public void onFailure(Throwable caught) {
			}
			public void onSuccess(Boolean result) {
				if(result){
					setInfoTextAndPO("מייל נשלח בהצלחה");
				}
				else
					setInfoTextAndPO("תקלה בשליחת המייל");
			}
		});
	}
	
	public void setType(String type) {
		request.setType(type);
		if(autoSearch)
			sendRequest();
	}

	public void setConnected(boolean connected) {
		this.connected = connected;
	}
	
	public boolean isConnected(){
		return connected;
	}
	
	public void setUserNick(String userNick){
		this.userNick = userNick;
		this.connected = true;
	}
	
	public String getUserNick(){
		return userNick;
	}
	
	public void updateUserIngsTable(String ingName){
		userIngsTable.addIngredient(ingName);
	}
	
	public void setRecipeInfoPanelRecipe(String link, String recipeName){
		recipeInfoPanel.setRecipe(link, recipeName);
	}
	
	public void setAutoSearch(boolean autoSearch){
		this.autoSearch = autoSearch;
	}
	
	public boolean isAutoSearch(){
		return autoSearch;
	}
	
	public boolean isSmallScreen(){
		return smallScreen;
	}
	
	public void setSmallScreen(){
		smallScreen = true;
		recipesTable.setSmaller();
		loginPanel.setSmaller();
		recipeInfoPanel.setSmaller();
		userIngsTable.setSmaller();
		fridge.setSmaller();
	}
	
	/** Popup a message and write it in the page bottom*/
	public void setInfoTextAndPO(String info){
		generalInfo.setText(info);
		quickPopup(info);		
	}
	
	public void setInfoText(String info){
		if (info.compareTo("d")==0)
			generalInfo.setText(defaultText);
		else
			generalInfo.setText(info);
	}
	
	public void setPO(String info){
		quickPopup(info);	
	}
	
	/** display a message to the user for a short time (1.5 second), and then hide it.
	 *  user can still use the UI while this is displayed */
	public void quickPopup(String msg){
		final DialogBox db = new DialogBox();	
		db.setText(msg);
		db.center();
		Timer t = new Timer() {
			public void run() {
				db.hide();
			}
		};
		t.schedule(1500);
	}
	
	/** display a message to the user for a longer time then the first one(2.5 second), and then hide it.
	 *  user can still use the UI while this is displayed */
	public void quickPopup2(String msg){
		final DialogBox db = new DialogBox();	
		db.setText(msg);
		db.center();
		Timer t = new Timer() {
			public void run() {
				db.hide();
			}
		};
		t.schedule(2500);
	}
	
	/** clear the recipes table, deletes all ings from request */
	public void clearSearch(){
		recipesTable.clear();
		recipesTable.hideNextBack();
		request.clearIngs();
		request.clearFilters();
		resetPage();
		setAutoSearch(false);
		userIngsTable.setAutoSearchChecker(false);
	}
	
	/** show "not log message" to user */
	public void showLogMessage(String content){
		logMessage.setText(logMessage.getText() + content);
		dBoxMes.show();
	}
	
	public List<String> getPopularRecipes(){
		return popularRecipes;
	}
	
	/** return the Top Viewed Recipes LIst*/
	public List<String> getTopViewed(){
		return popularRecipes;
	}
	
	/** welcome window to thge user */
	public void showWelcomePop(){
		final DialogBox welcomeBox = new DialogBox();
		VerticalPanel welcomePanel = new VerticalPanel();
		welcomeBox.setSize("200px", "200px");
		welcomeBox.setText("ברוכים הבאים");
		Label text;
		if(smallScreen) text = new Label("ברוכים הבאים לאתר כושרקוק. בכדי לקבל מידע על שימוש באתר הכנסו לאתר ולחצו על עזרה שבתחתית המסך. ההתחברות לאתר היא פשוטה ומהירה, אז קדימה הצטרפו אלינו ובתיאבון");
		else text = new Label("ברוכים הבאים לאתר כושרקוק. בכדי לקבל מידע על שימוש באתר הכנסו לאתר ולחצו על תמונת השף. ההתחברות לאתר היא פשוטה ומהירה, אז קדימה הצטרפו אלינו ובתיאבון");
		text.setStyleName("welcome-text");
		PushButton exitButton = new PushButton("הכנסו לאתר");
		exitButton.addStyleName("largeButtons");
		welcomePanel.add(text);
		welcomePanel.add(exitButton);
		welcomePanel.addStyleName("welcome");
		welcomePanel.setSpacing(8);
		welcomeBox.add(welcomePanel);
		welcomeBox.setGlassEnabled(true);
		welcomeBox.center();
		welcomeBox.show();
		exitButton.addClickHandler(new ClickHandler() {
			
			public void onClick(ClickEvent event) {
				welcomeBox.hide();
			}
		});	
	}
}
